package com.example.bbs.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.bbs.domain.entity.Comment;
import com.example.bbs.domain.entity.Post;
import com.example.bbs.domain.entity.User;
import com.example.bbs.domain.model.CommentDTO;
import com.example.bbs.domain.model.CommentVO;
import com.example.bbs.kafka.producer.CommentProducer;
import com.example.bbs.kafka.producer.PostProducer;
import com.example.bbs.mapper.CommentMapper;
import com.example.bbs.service.CommentService;
import com.example.bbs.service.PostService;
import com.example.bbs.service.UserService;
import com.example.bbs.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CommentServiceImpl implements CommentService {

    @Resource
    private CommentMapper commentMapper;
    @Resource
    private RedisTemplate commentInfoRedisTemplate;
    @Resource
    private RedisTemplate postInfoRedisTemplate;
    @Autowired
    private UserService userService;

    @Autowired
    private PostProducer postProducer;

    @Autowired
    private CommentProducer commentProducer;

    @Autowired
    private LikeServiceImpl likeServiceImpl;

    @Autowired
    private PostService postService;

    /**
     * 初始化操作
     */
    @PostConstruct
    private void init() {
        //加载最近两个月的评论信息到redis中
        LambdaQueryWrapper<Comment> queryWrapper = Wrappers.<Comment>lambdaQuery()
                .eq(Comment::getCreateTime, TimeUtil.getLastMonthDate(new Date(), 2));
        List<Comment> comments = commentMapper.selectList(queryWrapper);

        if (!CollectionUtils.isEmpty(comments)) {
            comments.forEach(e -> {

                //把Java对象转化为Map对象，存入redis的hash结构中
                Map<String, Object> map = JSONObject.parseObject(JSONObject.toJSONString(e), Map.class);
                User user = userService.getUserByUserId(e.getUserId());

                commentInfoRedisTemplate.opsForHash().putAll(e.getCommentId(), map);
                commentInfoRedisTemplate.opsForHash().put(e.getCommentId(), "user", user);

            });
        }
    }



    private Comment getCommentByIdFromCache(String commentId) {
        Map entries = commentInfoRedisTemplate.opsForHash().entries(commentId);
        if (CollectionUtils.isEmpty(entries)) {
            return null;
        }
        return Comment.parseMap(entries);
    }

    public Comment getCommentById(String commentId) {
        Comment comment = getCommentByIdFromCache(commentId);
        if (Objects.isNull(comment)) {
            comment = commentMapper.selectOne(Wrappers.<Comment>lambdaQuery().eq(Comment::getCommentId, comment));

            //异构入缓存
            Comment finalComment = comment;
            ThreadPoolUtil.executorService.execute(() -> {
                commentInfoRedisTemplate.opsForHash().putAll(commentId, Comment.parseEntry(finalComment));
            });

        }

        return comment;

    }


    public CommentDTO getCommentDTO(String commentId) {
        Comment comment = getCommentById(commentId);
        User user = userService.getUserByUserId(comment.getUserId());

        //查询点赞状态
        boolean likeStatus;
        User loginUser = HostHold.get();
        if (Objects.isNull(loginUser)) {
            likeStatus = false;
        } else {
            likeStatus = likeServiceImpl.isLikeComment(commentId, loginUser.getUserId());
        }

        return CommentDTO.builder()
                .comment(comment)
                .user(user)
                .likeStatus(likeStatus)
                .build();
    }


    @Override
    public void update(Comment comment) {
        LambdaUpdateWrapper<Comment> updateWrapper = Wrappers.<Comment>lambdaUpdate()
                .eq(Comment::getCommentId, comment.getCommentId());
        commentMapper.update(comment, updateWrapper);
    }

    /**
     * 新增评论
     *
     * @param comment
     */
    @Transactional
    public void saveCache(Comment comment) {
        comment.setUserId(HostHold.get().getUserId());
        comment.setCommentId(UniqueIdUtil.generateUniqueId(0));
        comment.setContent(comment.getContent());
        comment.setStatus(0);
        comment.setLikeCount(0);
        comment.setCreateTime(new Date());
        comment.setUpdateTime(new Date());
        comment.setHasRead(ConstantUtil.NOTICE_STATUS_UNREAD);

        commentInfoRedisTemplate.opsForHash().putAll(comment.getCommentId(), Comment.parseEntry(comment));

        //同步帖子评论数+1
        postInfoRedisTemplate.opsForHash().increment(comment.getPostId(), "commentCount", 1);

        //帖子新增回复, 加入到热帖集合中计算排行
        postService.addChangedPostSet(comment.getPostId());

        //发送帖子更新消息
        Post post = postService.getPostByPostId(comment.getPostId());
        postProducer.send("post-update", post);

        //发送新增评论消息
        commentProducer.send("comment-insert", comment);

    }


    public void save(Comment comment) {
        commentMapper.insert(comment);
    }

    /**
     * 批量查询某个帖子下的评论
     */
    public CommentVO getBatchCommentForPost(String postId, int page, int limit) {
        //如果帖子的评论数为0, 返回空集合
        Post post = postService.getPostByPostId(postId);
        if (post.getCommentCount() == 0) {
            return CommentVO.builder()
                    .totalSize(0)
                    .commentDTOList(Collections.EMPTY_LIST)
                    .build();
        }

        //先从缓存中查询该帖子下所有的评论数
        Set<String> keys = commentInfoRedisTemplate.keys("*");

        List<CommentDTO> commentDTOList = keys.stream()
                .filter(e -> postId.equals((String) commentInfoRedisTemplate.opsForHash().get(e, "postId")))
                .map(e -> getCommentDTO(e))
                .collect(Collectors.toList());
        //如果和post数据里的评论数不一致，从数据里重新查一次
        if (CollectionUtils.isEmpty(commentDTOList) || commentDTOList.size() != post.getCommentCount()) {
            List<Comment> commentList = commentMapper.selectList(Wrappers.<Comment>lambdaQuery().eq(Comment::getPostId, postId));
            commentDTOList = commentList.stream().map(e -> getCommentDTO(e.getCommentId())).collect(Collectors.toList());

        }
        //计算offset
        int totalSize = commentDTOList.size();
        int start = (page - 1) * limit;
        int end = start + limit;
        if (end > totalSize) {
            end = totalSize;
        }
        //按创建时间倒序排序
        commentDTOList.sort((a, b) -> (int) (b.getComment().getCreateTime().getTime() -
                a.getComment().getCreateTime().getTime()));
        //分页
        List<CommentDTO> commentDTOS = commentDTOList.subList(start, end);

        return CommentVO.builder()
                .totalSize(totalSize)
                .commentDTOList(commentDTOS)
                .build();
    }


}
